package OJPractice;

/*字符串和整数的拼接：1.“is”说明后边内容都会被强转成string  2. 100+5先得到105，然后和is拼接  3.先算括号内的*/
/*Java中是没有进程的概念的，jvm中的线程映射到操作系统中的进程（1 ：1）*/
/*在Java中声明一个数组时，不能直接限定数组长度，只有在实例化对象时，才能给定数组长度*/
/*call by value就是值传递，call by reference就是引用传递的意思*/
/*异常的捕获顺序：先捕获子类，再捕获父类*/
/*静态和父类的优先级是：静态>父类，先执行所有的静态的，再执行父类的构造代码块和构造函数，再执行子类的构
造代码块和构造函数*/
/*无符号右移是逻辑运算符，普通右移符号是算数运算符>>>,高位补0，>>高位补符号位 */
/*getDeclaredField：查找该Class所有声明属性(静态/非静态)，但是他不会去找实现的接口/父类的属性
getField：只查找该类public类型的属性，如果找不到则往上找他的接口、父类，依次往上，直到找到或者已经没有接口/父类*/
/*%运算符和/运算符是相同的,注意正负号的运算*/
/*接口中的成员变量默认都是静态常量，默认都是由public static final来修饰的*/
/*导包这可以导到当前层，不可以再导入包里面的包中的类*/
/*字符串相加只要有一个是非final类型的变量，编译器就不会进行优化，编译器优化：两个字符串字面常量相加：在编译时就直接将
字符串合并，而不是等到运行时再合并字符串*/
/*Java中的变量和基本数据类型的值都是存放在栈内存中，而new出来的对象本身存放在堆内存中，指向对象的引用还是存放在栈内存*/
/*栈内存的一个特点就是数据共享，这样设计是为了减少内存的消耗，==是判断两个引用所指向的地址是否是一样的*/
/*堆内存没有数据共享的特点，如果是string s= “hello”这个hello是存放在栈中的，new出来的对象才会存放在堆内存中*/
/*如果判断两个String对象是否相等用equals方法*/
/*threadlocalmap使用开放定址法解决hash冲突，hashmap使用链地址法解决hash冲突*/
/*对于父类中private的成员变量，只有类内部才能被访问，就算是这个父类的子类也不能访问，如果是子类的类型引用new的
子类的对象，此时要访问父类的私有成员变量还是不可以的，无法为其生成正确的字节码文件，注：此时是编译报错，不是运行出错*/
/*char在Java是两个字节，在C、C++中是一个字节：表示的数据范围：-128 ~ 127 */
/*Hashtable是一个哈希表，该类继承自Dictionary，*/
/*request.getAttribute()方法返回request范围存在的对象，而request.getParameter()方法是获取http提交过来的数据
 * ，getAttribute是返回对象，getParameter是返回字符串*/
/*以Java8为准，switch支持10种类型：byte，char，short，int，对于包装类：Byte，Short，character,Integer, String
 * enum  2.实际只支持int类型，（基本类型比int小的可以自动向上转型为int，基本类型对应的包装类型，自动拆箱机制拆成基本类型）
 * String比较的是string.hashCode值，enum类型，实际比较的是ordinal值（表示枚举值的顺序，这也是一个int类型）*/
/*匿名内部类：使用时必须继承一个类或者实现一个接口，匿名内部类没有名字，所以不能定义构造函数，匿名内部类中不能含有静态成员
 * 变量和静态方法*/
/*多线程代码一定要看清是普通的调用run方法还是启动线程，这俩代码执行的顺序是不一样的*/
/*显示初始化：用写的代码定义的初始化就是显示初始化，没有定义由编译器决定或者环境默认的初始化就是隐式初始化*/
/*在一个类中，并不是静态块最先初始化，而是静态域，而静态域中包含静态变量，静态块和静态方法，其中需要初始化的是静态变量和静态快，
 * 而他们两个的初始化顺序是靠他们俩的位置决定的*/
/*CopyOnWriteArrayList适用于读多写少的并发场景，ReadWriteLock是读写锁：写和写之间互斥，读和写之间互斥，读和读之间
可以并发执行。在读多写少的情况下可以提高效率。  ConcurrentHashMap是同步的HashMap，读写都加锁，volatile只能保证
多线程的可见性，不保证原子性*/
/*抽象类不是只能被继承，抽象类中可以有普通的方法并且被调用*/
/*String s1 = "java"，这个字符串是否创建新的对象是看这个字符串在常量池中有没有，如果有就直接指向常量池中的字符串，
 * 如果没有才会创建一个新的对象，String s2 = new String("java")这句代码一定会创建一个新的对象*/
/*字符串字面量（"java"）的加号拼接是在编译时期完成的拼接，而引用的字符串的加号进行拼接是在运行时期进行的，新创建的字符串存
放在堆中*/
/*垃圾回收（GC）主要针对的是堆区的回收，因为栈区的内存是随着给线程而释放的，堆区分为三个区：年轻代（Young Generation）
 * 年老代（Old Generation），永久代（Permanent Generation，也就是方法区）   年轻代：对象被创建时（new）的对象放在
 * Yong（除了一些占据内存比较大的对象），经过Minor GC（针对年轻代的内存回收）还活着的对象会被移动到年老代。
 * 年老代：就是上述年轻代移动过来的和一些比较大的对象，Major GC（FullGC）是针对年老代的回收。   永久代：存储的是final
 * 常量，static变量和常量池。*/
/*浮点数在内存中的32位不是简单的转换为十进制，而是通过公式计算而来，虽然通过公式只有4个字节，但是浮点数最大值比长整型的
 * 范围要大*/
/*StringBuffer和StringBuilder是不可以直接赋值一个字符串的，能直接赋值,可以直接赋值一个字符串的只有String*/
/*File类只能操作文件本身，但不能对文件内容进行修改，能够读写文件的是数据流*/

/*子类不可以继承父类的构造方法，只能用super()调用父类的方法*/
/*do while循环可能是无限次的，可能永远也 ！=0*/
/*Servlet的生命周期*/
/*标准的ASCII就是使用7个bit表示，扩展后的ASCII使用8个bit，ASCII码包含一些特殊空字符
* 所以不都是可打印字符*/
/*为了解决不同国家的编码冲突问题，Unicode产生，是使用两个字节表示一个字符，现代操作系统总大多数编程语言都直接
* 支持Unicode，Unicode中一个中文字符占两个字节，utf-8中一个中文字符占3个字节，在内存中，统一使用Unicode编码
* 当需要保存到硬盘中或者需要传输的时候，就转换为utf-8编码*/
/*Javac -d参数的用途是：指定编译后类层次的根目录*/
/*C中的‘a’是char类型，3是int类型，将int赋值给char属于从高向低赋值，错误*/
/*-d destination 目的地  -s source 起源地  javac -d 指定放置生成类文件的位置
* javac -s 指定放置生成源文件的位置*/
/*java和C++都有三个特征：封装，继承，多态*/
/*Java的垃圾回收机制不是程序结束时系统自动回收内存，GC的时间是不确定的*/
/*静态初始化块在类加载时完成，无法人为调用*/
/*常量在进行算数运算的时候，在编译时期就已经算好了，就已经赋值了*/
/*所有的异常都是Exception的子类，如果在try catch语句中先写Exception，把Exception放在前边
* 捕获，后边的catch将永远不执行，所以父类永远放在最后边*/
/*Java线程是有优先级的*/
/*执行对象实例化的过程中遵循多态性，调用的方法都是将要实例化的子类中重写的方法，只有明确调用了super.xx
* 或者是子类中没有该方法时，才会调用父类中相同名称的方法。*/
/*int类型和Integer类型比较时，先将Integer拆箱*/
/*值传递和引用传递：Java中原始数据类型都是值传递，传递的是值的副本，形参的改变不会影响实际参数的值，
* 引用传递传递的是引用类型数据，包括String，数组，列表，map，类对象等类型，形参和实参指向的是统一内存的
* 地址，因此形参改变会影响实参的值*/
/*DDL是更改数据库的结构（数据库的定义语言），DML是管理数据库中的数据（数据库的操作语言）*/
/*影响上的区别：DDL命令会影响整个数据库或表，但DML命令会影响表中的一个或者多个记录
* 回滚上的区别：带有DDL命令的sql语句无法回滚，带有DML命令的语句可以回滚*/
/*DDL会自动提交事务*/
/*取模操作只适用于整形，虽然float和double类型都可以使用，但是结果不一定正确*/
/*final修饰的方法可以被重载，但是不能被重写*/
/*yield（）让当前正在运行的线程回到可运行状态，以允许具有相同优先级的其他线程获得运行的机会，因此，使用yield方法的
* 目的就是让具有相同优先级的线程之间能够适当的轮换执行，但是，实际中无法保证yield（）达到让步的目的，因为，
* 让步的线程可能被线程调度程序再次选中。*/
/*yield方法是让优先级大于等于自己的其他线程获得同等运行的机会，具体是谁抢到就不一定了，或者说使用yield方法就是
* 使自己和优先级大于等于自己的线程回到统一起跑线上，然后就不能保证谁一定跑的快到达中带你获得执行权了*/
/*transient变量和序列化有关，起标记作用，具体的序列化由ObjectOutputStream和ObjectOutputStream完成，
* transient修饰的变量不能被序列化，static变量加没加transient都不可以被序列化*/
/*java程序内存泄露的最直接表现是程序抛内存一处的Exception*/
/**/
/*strlen统计字符串长度，遇到\0结束，且不统计\0。sizeOf计算开辟空间的大小，a数组开辟的空间大小，a数组
* 开辟的空间为6，所以本体的sizeOf（a）= 6；strlen（a）= 4 */
/*strlen计算字符串长度，遇到\0结束，且不统计\0，sizeof计算物理空间大小*/
/*有一颗三叉树，度为123的节点数分别为n1，n2，n3，则该三叉树的叶子节点数n0为多少？
* 设这棵树总结点数为n，边数为B，此时 1.B = n - 1     2.同时总结点数为n = n0 + n1 + n2 + n3
* 3.边数B = （n0 + n1 + n2 + n3）- 1
* 4.而树有这样的性质：有一个度为1的节点就有一条边，有一个度为2的节点就有两条边，依次类推
* 边数：B = n1 + 2n2 + 3n3    5.n0 = n2 + 2*n3 + 1*/
/*B树删除一个关键字的方法是一定要保证删除之后的B树的关键字还是有序的，而且左中右三个区间 都是有关键字的*/

import javax.swing.text.html.HTMLEditorKit;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.StringTokenizer;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.locks.ReentrantLock;

//对面试题的总结******
public class NewCodeSelect {



   static ReentrantLock reentrantLock = new ReentrantLock();
    public static void main(String[] args) {
        //ReentrantLock中的公平锁和非公平锁的底层实现
        /*首先不管是公平锁还是非公平锁，他们的底层实现原理都会使用AQS来进行排队，他们的区别在于：线程
        * 在使用lock()方法加锁时，如果是公平锁，会先检查AQS队列中是否存在线程在排队，如果有线程在排队，
        * 则当前线程也进行排队，如果是非公锁，则不会去检查是否有线程在排队，而是直接竞争锁。
        *
        * 不管是公平锁还是非公平锁，一旦没有竞争到锁，都会进行排队，当锁释放时，都是在唤醒排在最前面的线程
        * 所以非公平锁只是体现在了线程加锁阶段，而没有体现在线程倍唤醒阶段。
        *
        * 另外ReentrantLock是可重入锁，不管是公平锁还是非公平锁都是可重入的*/
        reentrantLock.lock();

        reentrantLock.lock();

        /*ReentrantLock默认是非公平锁，这样去插队看锁是否在被竞争的状态下可能效率会更高一点，
        * 但是就损害了这种公平性。
        * 可重入锁：同一个线程如果同时调用lock方法，此时不会阻塞，只要是同一个线程，就是开始的时候调用
        * lock()方法已经拿到锁了，再去调用lock()方法，此时也不会进行阻塞
        * 还有就是需要注意： 如果加锁两次，最后就需要进行解锁，就需要解锁两次才能完全的释放锁。
        * */
    }

    private static ThreadLocal<String> s = new ThreadLocal<>();
    public static void main9(String[] args) throws InterruptedException {
        Thread t1 = new Thread(()-> {
            s.set("jajaja");
            String s2 = s.get();
            System.out.println(s2);
        });
        s.set("jiaoao");
        String s1 = s.get();
        System.out.println(s1);
        //ThreadLocal的底层原理
        /*1.ThreadLocal是Java中所提供的线程本地存储机制，可以利用该机制将数据缓存在某个线程内部，该线程
         * 可以在任意时刻，任意方法中获取缓存的数据
         * 2.ThreadLocal底层是通过ThreadLocal会造成内存泄露，因为当ThreadLocal对象使用完之后，应该把
         * 设置的key，value，也就是Entry对象进行回收，但是线程池中的线程不会回收，而线程对象是通过强引用
         * 指向Entry对象，线程不被回收，Entry对象也就不会被回收，从而出现内存泄露，解决办法是在使用了ThreadLocal对象
         * 之后，手动调用ThreadLocal的remove方法，手动清除Entry对象，
         * 4.ThreadLocal经典的应用场景就是连接管理（一个线程持有一个连接，该连接对象可以在不同的方法之间进行传递，
         * 线程之间不共享同一个连接）*/
    }
    public static void main8(String[] args) {
        //ConcurrentHashMap的扩容机制
        /*1.7版本
        * 1.ConcurrentHashMap是基于Segment分段实现的（Segment对象中也是一个数组，数组下标元素存放的元素
        * 是链表），在扩容时是单独判断的，一个Segment进行扩容不会影响其他Segment对象
        * 2.每个Segment相对于一个小型的HashMap 3.每个Segment内部进行扩容，和HashMap的扩容逻辑类似
        * 4.先生成新的数组，然后转移元素到新数组中
        * 5.扩容的判断也是每个Segment内部单独判断的，判断是否超过阈值
        * 1.8版本
        * 1.ConcurrentHashMap不再基于Segment实现
        * 2.当某个线程进行put时，如果发现ConcurrentHashMap正在进行扩容，那么该线程一起进行扩容
        * 3.如果某个线程put时，发现没有正在进行扩容，则将key-value添加到ConcurrentHashMap中，
        * 然后判断是否超过阈值，超过了就进行扩容（多线程在进行扩容时，每个线程都有自己负责部分元素的转移
        * 此时多线程扩容是分开的，互不影响的）（如何扩容：和hashMap的扩容是一样的）
        * 4.ConcurrentHashMap时支持多个线程同时扩容的
        * 5.在扩容之前也是先生成一个新的数组
        * 6.在转移元素时，先将原数组分组，将每组分给不同的线程来进行元素的转移，每个线程负责一组或者
        * 多组的元素转移工作
        * */
        ConcurrentHashMap<String,Integer> map = new ConcurrentHashMap<>();

    }
    public static void main7(String[] args) {
        //HashMap的扩容机制（1.7和1.8的扩容机制是不一样的）
        //1.7的HashMap是数组 + 链表   1.8的HashMap是数组 + 链表 + 红黑树
        HashMap<String, String> hashMap = new HashMap<>();
        hashMap.put("jiaoao","ja");
        /*1.7的put方法在添加元素之前先判断是否要进行扩容，如果需要扩容，
        * 先new一个新数组，然后转移元素（双层循环）数组的每一个元素都是一个链表，
        * 当数组下标中元素不为空的时候就遍历这个链表，然后一直取下一个元素
        * 遍历每一个元素的时候，按照当前数组的哈希值和新数组的下标重新计算新的hash值
        * 然后再把当前数组中的这个元素转移到新数组中
        * */
        System.out.println(hashMap);
        /*1,7版本
        * 1.先生成新数组   2.遍历老数组中的每个位置上的链表上的每个元素
        * 3.取每个元素的key，并基于新数组长度，计算出每个元素在新数组中的下标
        * 4.将元素添加到新数组中去  5.所有元素转移完了之后，将新数组赋值给HashMap对象的table属性
        * 1.8版本
        * 1.先生成新数组  2.遍历老数组中的每个位置上的链表或者红黑树
        * 3.如果是链表，则直接将链表中的每个元素重新计算下标，并添加到新数组中去
        * 4.如果是红黑树，就先遍历红黑树，先计算出红黑树中每个元素对应在新数组中的下标位置
        *  a、统计每个下标位置的元素个数
        *  b、如果该位置下的元素个数超过了8，则生成一个新的红黑树，并将根节点的添加到新数组的对应位置
        *  c、如果该位置的元素个数没有超过8，此时生成一个链表，并将链表中的头节点添加到新数组的对应位置
        * 5.所有元素转移完了之后，将新数组赋值给HashMap对象的table属性*/
    }
    public static void main6(String[] args) {
        //copyOnWriteArrayList底层实现的原理
        //首先CopyOnWriteArrayList内部也是数组实现的，在添加元素时，会复制一个新数组，写操作在新数组
        //上进行，读操作在原数组上进行。
        //2. 并且写操作会加锁，防止出现写入数据丢失的问题
        //3. 写操作之后会把原数组指向新数组
        //4. CopyOnWriteArrayList允许在写擦欧总时来读数据，大大提高了读的性能，因此适合读多写少的场景
        //但是CopyOnWriteArrayList会比较占内存，同时可能读到的数据不是实时最新的数据，所以不合适实时性
        //要求高的场景
        ArrayList<String> arrayList = new ArrayList<>();
        arrayList.add("zhangsan");
        arrayList.add("lissi");
        //在添加两个数据的时候，如果是多线程操作，此时就是线程不安全的，可能会造成数据的丢失。
        CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
        //此时的CopyOnWriteArrayList类就是安全的，在add元素的时候，先加一个锁，然后将底层的数组
        //拿出来，之后再复制一个数组，长度+1，然后赋值，之后将新数组赋值给原来的数组，然后解锁。
        //所以CopyOnWriteArrayList是线程安全的
        list.add("zhouyu");
        String s = list.get(1);
        System.out.println(list);
        System.out.println(arrayList);
    }
    public static void main5(String[] args) {
        /*ArrayList和LinkedList的区别：他们都实现了List的接口，
        * 不同点：1. ArrayList是实现了RandomAccess接口的，也就是支持随机访问的，而LinkedList是不支持
        * 随机访问的。
        * 2. LinkedList类还实现了Deque接口，所以此时LinkedList还可以当作一个双端队列或者队列来使用，
        * 而ArrayList没有
        * 3.ArrayList是数组实现的，LinkedList是链表实现的，所以有一个特点就是数组就是查询块，而增加
        * 删除元素就效率较低，LinkedList对于增加和删除元素数据效率较高，但是也不是绝对的，如果数组添加元素
        * 的时候正好需要进行扩容，此时效率较低，还有就是如果给指定的下标位置进行添加元素，此时需要System.ArrayCopy
        * 方法对数组元素往后挪动，此时效率也不会很高，  对于链表，也有指定的位置进行添加元素，此时需要链表的节点的
        * 遍历，此时也会影响添加元素的性能*/
        ArrayList<String> arrayList = new ArrayList<>();
        arrayList.add("1");//扩容
        arrayList.add(3,"1");//扩容，元素的移动

        LinkedList<String> linkedList = new LinkedList<>();
        linkedList.add("1");
        linkedList.add(3,"1");
    }
    public static void main4(String[] args) {
        String str = "abc";
        str = "abcd";//此时并没有修改str对象，而是产生了一个新的”abcd“字符串对象
        //因为String类的对象是不可变的
        StringBuilder builder = new StringBuilder("abc");
        builder.append("abc");
        System.out.println(builder);
        //而StringBuilder类和StringBuffer类的对象是可以改变的，可以在原来的字符串上进行修改
        StringBuffer buffer = new StringBuffer("111");
        buffer.append("222");
        //StringBuffer是线程安全的，StringBuilder是线程不安全的，所以在单线程环境下可以使用StringBuilder
        //效率是更高的
    }
    public static void main3(String[] args) {
        /*在Integer类中，存在一个静态内部类IntegerCache，该类中存在一个Integer cache[],并且存在一个
        * static块，会在加载类的时候执行，会将-128到127数字提前生成Integer对象，并缓存在cache数组中
        * ，当我们在定义Integer数字时，会调用到Integer的valueOf方法，valOf方法会判断所定义的数字是否在
        * 这个区间内，如果在则直接从cache数字中获取Integer对象，如果不在这个范围内，则生成一个新的Integer
        * 对象*/
        Integer a1 = 100;
        Integer a2 = 100;
        System.out.println(a1 == a2);
        Integer b1 = 128;
        Integer b2 = 128;
        System.out.println(b1 == b2);
    }
    public static void main2(String[] args) {
        String s1 = new String("abc");
        //上一行代码会产生两个对象，一个是abc字符串字面常量（在字符串常量池中），
        //一个是new String对象，new的对象都在堆中，
        String s2 = "abc";
        System.out.println(s1 == s2);//false
        String s3 = s1.intern();
        System.out.println(s2 == s3);//true
        //String对象的intern方法，首先会检查字符串常量池中是否存在"abc"
        //如果存在则返回该字符串引用，如果不存在，则把"abc"添加到字符串常量池中
        //并返回该字符串的引用
    }
    public static void main1(String[] args) {
        String s1 = new String("ja");
        String s2 = new String("ja");
        System.out.println(s1.equals(s2));//false
        String s3 = "ja";
        String s4 = "ja";
        System.out.println(s3.equals(s4));//true
        System.out.println(s3.equals(s1));//false
        String s5 = "jaja";
        String s6 = s3 + s4;
        System.out.println(s5.equals(s6));//false
        final String s7 = "ja";
        final String s8 = "ja";
        String s9 = s7 + s8;
        System.out.println(s5.equals(s9));//true
        String s10 = "ja" + "ja";
        System.out.println(s5.equals(s10));//true
    }
}
